package main

import (
	"fmt"
	"gobase/app/controller"
	"gobase/util"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/casbin/casbin"
	xormadapter "github.com/casbin/xorm-adapter"
	nice "github.com/ekyoung/gin-nice-recovery"
	"github.com/gin-contrib/gzip"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/xormplus/xorm"
)

var (
	rxURL     = regexp.MustCompile(`^/regexp\d*`)
	store     sessions.Store
	appConfig map[string]string
	dbConfig  map[string]string
	engine    *xorm.Engine
	casbinE   *casbin.Enforcer
)

//然后在init函数中初始化
func init() {
	//初始化配置文件
	util.InitConfig()
	appConfig = util.GetConfig("app")

	//session maxAge cookie时间 86400=24*3600 1天
	store = cookie.NewStore([]byte("secret"))
	store.Options(sessions.Options{
		MaxAge: 3600,
		Path:   "/",
	})

	//数据库
	dbConfig = util.GetConfig("database")
	db, err := xorm.NewEngine(dbConfig["drivername"], dbConfig["datasource"])
	if err != nil {
		fmt.Println("打开数据库连接失败: ", err)
		os.Exit(0)
	}
	engine = db
	engine.SetMaxIdleConns(5)
	engine.SetMaxOpenConns(20)
	engine.ShowSQL(true)
	engine.ShowExecTime(true)

	//casbin 权限管理
	xadapter := xormadapter.NewAdapter(dbConfig["drivername"], dbConfig["datasource"], true)
	casbinE = casbin.NewEnforcer("config/rbac_model.conf", xadapter)
	casbinE.EnableLog(true)
	casbinE.EnableAutoSave(true)
}

func main() {
	gin.SetMode(appConfig["mode"])
	router := gin.New()

	//静态文件
	router.SetFuncMap(util.GetFuncMap())
	router.Static("/static", "./asset/")
	router.StaticFile("/favicon.ico", "./favicon.ico")
	//页面路径
	router.LoadHTMLGlob("app/view/**/*")
	router.Delims("{{", "}}")
	router.NoRoute(util.NotFound404)
	router.NoMethod(util.NotFound404)

	//中间件
	initMiddleware(router)
	//注册路由
	initRouter(router)

	router.Run(":" + appConfig["port"])
}

//注册路由
func initRouter(router *gin.Engine) {
	new(controller.IndexController).Router(router)
	new(controller.LoginController).Router(router)
	new(controller.UserController).Router(router)
	new(controller.RoleController).Router(router)
	new(controller.ResourceController).Router(router)
	new(controller.OrgController).Router(router)
}

//注册中间件
func initMiddleware(router *gin.Engine) {
	//日志
	router.Use(util.Logger())
	//错误处理
	router.Use(nice.Recovery(util.Recovery500))
	//gzip
	router.Use(gzip.Gzip(gzip.DefaultCompression))

	//session
	router.Use(sessions.Sessions(util.SessionIDName, store))
	//身份验证（登录验证）
	router.Use(loginAuth())
	//权限验证
	router.Use(casbinAuth())

	//gin.Context传数据
	router.Use(passVar(engine, casbinE))
}

func passVar(db *xorm.Engine, e *casbin.Enforcer) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Set(util.DatabaseEngine, db)
		c.Set(util.CasbinEnforcer, e)
		c.Next()
	}
}

func loginAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		//获取session
		user := util.GetCurrLoginUser(c)
		uid := util.GetCurrLoginUID(c)
		isLogin := (user != "" && uid > 0)
		reqURL := c.Request.URL.Path

		if strings.Index(reqURL, "/admin") < 0 && (reqURL != "/index" && reqURL != "/") {
			c.Next()
			return
		}

		//判断是否登录
		if !isLogin {
			c.Redirect(http.StatusFound, "/login/index")
		} else if isLogin && (reqURL == "/login/index" || reqURL == "/login/login") {
			//限制重复登录
			c.Redirect(http.StatusFound, "/index")
		} else {
			c.Next()
		}
	}
}

func casbinAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		uidStr := strconv.FormatInt(util.GetCurrLoginUID(c), 10)
		method := c.Request.Method
		path := c.Request.URL.Path

		if strings.Index(path, "/admin") >= 0 &&
			uidStr != "1" &&
			!casbinE.Enforce(uidStr, path, method) {

			c.JSON(http.StatusUnauthorized, gin.H{
				"status":  false,
				"message": "没有操作权限!",
			})
			c.Abort()
			return
		} else {
			c.Next()
		}
	}
}
